from abc import ABC, abstractmethod
from typing import List, Dict, Any
from django.db import models
from django.contrib.auth.models import User
from .models import FoodItem, UserProfile
import random


class DietRecommendationStrategy(ABC):
    """饮食推荐策略接口"""

    @abstractmethod
    def generate_diet_plan(self, user_profile: UserProfile, days: int = 7) -> Dict[str, Any]:
        """
        生成饮食计划
        :param user_profile: 用户档案
        :param days: 计划天数，默认为7天
        :return: 包含饮食计划详情的字典
        """
        pass

    def _calculate_nutrient_goals(self, user_profile: UserProfile) -> Dict[str, float]:
        """计算营养目标（基础实现，可被具体策略覆盖）"""
        calorie_target = user_profile.calculate_calorie_target() or 2000

        # 根据健身目标调整宏量营养素比例
        if user_profile.fitness_goal == 'weight_loss':
            # 减脂: 高蛋白，中等脂肪，低碳水
            protein_ratio = 0.35
            fat_ratio = 0.30
            carb_ratio = 0.35
        elif user_profile.fitness_goal == 'muscle_gain':
            # 增肌: 高蛋白，高碳水，中等脂肪
            protein_ratio = 0.30
            fat_ratio = 0.25
            carb_ratio = 0.45
        else:
            # 保持健康: 均衡比例
            protein_ratio = 0.25
            fat_ratio = 0.25
            carb_ratio = 0.50

        # 计算具体克数 (1g蛋白质=4卡, 1g碳水=4卡, 1g脂肪=9卡)
        protein_g = (calorie_target * protein_ratio) / 4
        fat_g = (calorie_target * fat_ratio) / 9
        carb_g = (calorie_target * carb_ratio) / 4

        return {
            'calories': calorie_target,
            'protein': protein_g,
            'fat': fat_g,
            'carbs': carb_g
        }

    def _get_suitable_foods(self, user_profile: UserProfile) -> models.QuerySet:
        """获取适合用户饮食偏好的食物"""
        preference = user_profile.dietary_preference
        
        # 如果用户偏好是regular，返回所有食物
        if preference == 'regular':
            return FoodItem.objects.all()
        
        # 否则按偏好筛选，但也包含regular的食物
        return FoodItem.objects.filter(
            models.Q(suitable_for=preference) | 
            models.Q(suitable_for='regular')
        )

    def _generate_meal(self, foods: models.QuerySet, meal_type: str,
                       calorie_target: int, nutrient_goals: Dict[str, float]) -> List[FoodItem]:
        """生成单餐食谱（基础实现）"""
        # 筛选适合该餐型的食物
        meal_foods = foods.filter(category=meal_type)

        if not meal_foods.exists():
            return []

        # 将QuerySet转换为列表以便使用random.choice
        meal_foods_list = list(meal_foods)

        selected_foods = []
        current_calories = 0
        max_calories = calorie_target
        attempts = 0
        max_attempts = 20  # 最大尝试次数

        # 简单随机选择食物，直到达到热量目标
        while current_calories < max_calories and meal_foods_list and attempts < max_attempts:
            # 随机选择一个食物
            food = random.choice(meal_foods_list)
            attempts += 1

            # 如果添加这个食物不会超过目标太多，则添加
            if current_calories + food.calories <= max_calories * 1.2:
                selected_foods.append(food)
                current_calories += food.calories

            # 防止无限循环
            if len(selected_foods) > 10:
                break

        return selected_foods


class WeightLossVegetarianStrategy(DietRecommendationStrategy):
    """减脂素食策略"""

    def generate_diet_plan(self, user_profile: UserProfile, days: int = 7) -> Dict[str, Any]:
        nutrient_goals = self._calculate_nutrient_goals(user_profile)
        suitable_foods = self._get_suitable_foods(user_profile)

        # 为减脂调整营养目标
        nutrient_goals['calories'] = max(nutrient_goals['calories'] - 200, 1200)

        # 生成多天的饮食计划
        plan = {
            'nutrient_goals': nutrient_goals,
            'daily_meals': [],
            'strategy_name': 'WeightLossVegetarian'
        }

        for day in range(days):
            daily_calorie_target = nutrient_goals['calories']

            # 分配各餐热量 (早餐25%, 午餐35%, 晚餐30%, 零食10%)
            breakfast_target = daily_calorie_target * 0.25
            lunch_target = daily_calorie_target * 0.35
            dinner_target = daily_calorie_target * 0.30
            snack_target = daily_calorie_target * 0.10

            daily_meal = {
                'day': day + 1,
                'breakfast': self._generate_meal(suitable_foods, 'breakfast', breakfast_target, nutrient_goals),
                'lunch': self._generate_meal(suitable_foods, 'lunch', lunch_target, nutrient_goals),
                'dinner': self._generate_meal(suitable_foods, 'dinner', dinner_target, nutrient_goals),
                'snacks': self._generate_meal(suitable_foods, 'snack', snack_target, nutrient_goals),
            }

            plan['daily_meals'].append(daily_meal)

        return plan


class MuscleGainHighProteinStrategy(DietRecommendationStrategy):
    """增肌高蛋白策略"""

    def generate_diet_plan(self, user_profile: UserProfile, days: int = 7) -> Dict[str, Any]:
        nutrient_goals = self._calculate_nutrient_goals(user_profile)
        suitable_foods = self._get_suitable_foods(user_profile)

        # 为增肌调整营养目标
        nutrient_goals['calories'] = nutrient_goals['calories'] + 300
        nutrient_goals['protein'] = nutrient_goals['protein'] * 1.2  # 增加蛋白质比例

        # 生成多天的饮食计划
        plan = {
            'nutrient_goals': nutrient_goals,
            'daily_meals': [],
            'strategy_name': 'MuscleGainHighProtein'
        }

        for day in range(days):
            daily_calorie_target = nutrient_goals['calories']

            # 分配各餐热量 (早餐30%, 午餐35%, 晚餐30%, 零食5%)
            breakfast_target = daily_calorie_target * 0.30
            lunch_target = daily_calorie_target * 0.35
            dinner_target = daily_calorie_target * 0.30
            snack_target = daily_calorie_target * 0.05

            daily_meal = {
                'day': day + 1,
                'breakfast': self._generate_meal(suitable_foods, 'breakfast', breakfast_target, nutrient_goals),
                'lunch': self._generate_meal(suitable_foods, 'lunch', lunch_target, nutrient_goals),
                'dinner': self._generate_meal(suitable_foods, 'dinner', dinner_target, nutrient_goals),
                'snacks': self._generate_meal(suitable_foods, 'snack', snack_target, nutrient_goals),
            }

            plan['daily_meals'].append(daily_meal)

        return plan


class BalancedLowCarbStrategy(DietRecommendationStrategy):
    """均衡低碳水策略"""

    def generate_diet_plan(self, user_profile: UserProfile, days: int = 7) -> Dict[str, Any]:
        nutrient_goals = self._calculate_nutrient_goals(user_profile)
        suitable_foods = self._get_suitable_foods(user_profile)

        # 为低碳水调整营养目标
        nutrient_goals['carbs'] = nutrient_goals['carbs'] * 0.6  # 减少碳水比例
        nutrient_goals['fat'] = nutrient_goals['fat'] * 1.2  # 增加脂肪比例

        # 生成多天的饮食计划
        plan = {
            'nutrient_goals': nutrient_goals,
            'daily_meals': [],
            'strategy_name': 'BalancedLowCarb'
        }

        for day in range(days):
            daily_calorie_target = nutrient_goals['calories']

            # 分配各餐热量 (早餐25%, 午餐40%, 晚餐30%, 零食5%)
            breakfast_target = daily_calorie_target * 0.25
            lunch_target = daily_calorie_target * 0.40
            dinner_target = daily_calorie_target * 0.30
            snack_target = daily_calorie_target * 0.05

            daily_meal = {
                'day': day + 1,
                'breakfast': self._generate_meal(suitable_foods, 'breakfast', breakfast_target, nutrient_goals),
                'lunch': self._generate_meal(suitable_foods, 'lunch', lunch_target, nutrient_goals),
                'dinner': self._generate_meal(suitable_foods, 'dinner', dinner_target, nutrient_goals),
                'snacks': self._generate_meal(suitable_foods, 'snack', snack_target, nutrient_goals),
            }

            plan['daily_meals'].append(daily_meal)

        return plan


# 策略工厂：根据用户目标和个人偏好选择合适的策略
class DietStrategyFactory:
    @staticmethod
    def get_strategy(user_profile: UserProfile) -> DietRecommendationStrategy:
        goal = user_profile.fitness_goal
        preference = user_profile.dietary_preference

        # 根据用户目标和饮食偏好选择策略
        if goal == 'weight_loss' and preference in ['vegetarian', 'vegan']:
            return WeightLossVegetarianStrategy()
        elif goal == 'muscle_gain':
            return MuscleGainHighProteinStrategy()
        elif preference == 'low_carb':
            return BalancedLowCarbStrategy()
        else:
            # 默认策略
            return BalancedLowCarbStrategy()